#ifndef LOGIC_H
#define LOGIC_H

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <ctype.h>
#include <algorithm>
#include <fstream>

#include "Parser.h"
#include "Data.h"
#include "Task.h"
#include "Writer.h"

#include "boost/date_time/posix_time/posix_time.hpp"

using namespace std;
using namespace boost::posix_time;
using namespace boost::gregorian;

class Logic {
	
public:
	// execution for UI
	string executeUserCommand(string userCommand, vector<Task>& tasksVector);

	Logic();
	~Logic();

private:
	// LOGIC MESSAGE
	static const string INVALID_COMMAND_MESSAGE;

	// FILE NAME
	static const string FILE_NAME;

	// TASK MESSAGE
	static const string ADD_SUCCESS_MESSAGE;
	static const string DELETE_SUCCESS_MESSAGE;
	static const string CLEAR_SUCCESS_MESSAGE;
	static const string LIST_SUCCESS_MESSAGE;
	static const string SORT_SUCCESS_MESSAGE;
	static const string SORT_INTERNAL_SUCCESS_MESSAGE;
	static const string SORT_ALPHA_SUCCESS_MESSAGE;
	static const string SORT_FLAG_SUCCESS_MESSAGE;
	static const string SORT_DATE_TIME_SUCCESS_MESSAGE;
	static const string EDIT_SUCCESS_MESSAGE;
	static const string DONE_SUCCESS_MESSAGE;
	static const string UNDONE_SUCCESS_MESSAGE;
	static const string SEARCH_SUCCESS_MESSAGE;
	static const string FLAG_SUCCESS_MESSAGE;
	static const string UNFLAG_SUCCESS_MESSAGE;
	static const string UNDO_SUCCESS_MESSAGE;

	// ERROR MESSAGES
	static const string ADD_ERROR_MESSAGE;
	static const string ADD_EXIST_ERROR_MESSAGE;
	static const string ADD_ERROR_CLASH_MESSAGE;
	static const string DELETE_ERROR_MESSAGE;
	static const string CLEAR_ERROR_MESSAGE;
	static const string LIST_ERROR_MESSAGE;
	static const string SORT_ERROR_MESSAGE;
	static const string SORT_FLAG_ERROR_MESSAGE;
	static const string SORT_DATE_TIME_ERROR_MESSAGE;
	static const string SORT_DONE_ERROR_MESSAGE;
	static const string EDIT_ERROR_MESSAGE;
	static const string DONE_ERROR_MESSAGE;
	static const string DONE_MULTIPLE_MATCH_ERROR_MESSAGE;
	static const string FLAG_ERROR_MESSAGE;
	static const string UNFLAG_ERROR_MESSAGE;
	static const string UNDO_ERROR_MESSAGE;
	static const string NO_CHANGES_ERROR_MESSAGE;
	static const string NOT_FOUND_ERROR_MESSAGE;
	static const string INDEX_OUT_OF_BOUNDS_ERROR_MESSAGE;

	// ERROR LOG FILE NAME
	static const string ERROR_LOG_FILE_NAME;
	static const string ERROR_LOG_FILE_EOF;

	// SUB WRITER FILE NAME
	static const string SUB_WRITER_FILE_NAME;
	static const string FILE_NAME_EXTENSION;

	// UNDO STATES
	static const int INIT_UNDO_STATE;
	static const int MAX_UNDO_STATES;

	// KEYWORD
	static const string DELETE_TYPE_DONE;

	// POSITIONS
	static const int FOUND_ONE_MATCH;
	static const int FIRST_POSITION;

	// TASK RELATED
	static const string APPEND_DUPLICATED_OPEN;
	static const string APPEND_DUPLICATED_CLOSE;

	// DELETE TYPE
	enum DeleteType {
		INDEX,
		NAME,
		SEARCH_TERM,
		DONE,
	};

	Parser* _parser;
	Data* _data;
	Writer* _writer;
	Task _task;

	// support up to 10 undo
	// 11 writer states (including the main _writer)
	// cycles through the 10 states
	int _undoState;
	Writer* states[10];

	// add
	int isExistName(string taskName);
	bool isDateClash(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string rectifyRepeatName(int repeats, string newTaskName, int append, string taskName);
	string addFloatingTask(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string addDeadlineTask(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string addTimedTask(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);

	// delete
	bool isDeleteByDone(string term); // returns true if matches done keyword and false otherwise
	bool isDeleteByName(string term); // returns true if found in vector and false otherwise
	bool isDeleteBySearchTerm(string term); // returns true if found in vector and false otherwise
	string deleteTaskByIndex(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string deleteDoneTask(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string deleteTaskByName(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string deleteTaskByString(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string deleteTaskBySearchTerm(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);

	// clear
	string clearTask(vector<Task>& tasksVector);

	// list
	string listAllTasks(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string listTaskBySearchTerm(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string listTaskByDay(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string listTaskByDate(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string listTaskNextWeek(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string listTaskByMonth(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string listTaskByDone(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);

	// edit
	string editTaskByName(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string editTaskByStartTime(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string editTaskByEndTime(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string editTaskByStartDateTime(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string editTaskByEndDateTime(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);

	// sort
	string sortTasksAlphabetically(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string sortTasksByFlag(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string sortTasksByDateTime(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string sortTasksByDone(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);

	// flag
	string flagTaskByName(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string flagTaskByIndex(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);

	// done
	string doneByIndex(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string doneBySearchTerm(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);

	// help
	string help(Parser::PARSED_COMMAND parsedCommand);

	// undo
	void updateState();
	bool updateUndoState();
	int getUndoState();
	void updateMainWriter(int undoState);
	string undo(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);

	// log error messages
	void logErrors(string errorMessage, int undoState);

	// setup
	void setupMainWriter();
	void setupData();
	void setupParser();
	void setupWriters();
	void setInitUndoState(int initUndoState);
	void populateData();

	// helper functions
	void emptyTasksVector(vector<Task>& tasksVector);
	void rePopulateUIVector(vector<Task>& tasks, vector<Task>& tasksVector);
	bool isOutOfBounds(int index, int size);
	bool isMultipleMatch(int size);

	// for sorting
	bool sortAlpha(Task t1, Task t2); // sort by name : ascending
	struct sortA {
		bool operator() (Task t1, Task t2) { return t1.getTaskName() < t2.getTaskName(); };
	} sortObjAlpha;
	bool sortDateTime(Task t1, Task t2); // sort by date : ascending
	struct sortT {
		bool operator() (Task t1, Task t2) { return t1.getStartTime() < t2.getStartTime(); };
	} sortObjDateTime;
	void sortByDone(vector<Task>& tasksVector); // sort by done|not done
	void sortByFlag(vector<Task>& tasksVector); // sort by flagged|not flagged

	// redundant functions : to be removed
	void copyVectorContents(vector<Task>& fromVect, vector<Task>& toVect);
};

#endif ILOGIC_H